﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;

namespace Marmat.Forms
{
    /// <summary>
    /// 封装 实体的数据源
    /// </summary>
    /// <typeparam name="Entity"></typeparam>
    [Designer("Marmat.Forms.EntityDataSourceDesigner")]
    public class EntityDataSource<Entity> : Component, IBindingSource, IBindingFieldNotificationBus
    {
        public EntityDataSource()
        {
            this.bindingSource = new BindingSource();
        }

        private bool _editable = true;

        [DefaultValue(true)]
        public virtual bool AllowEdit
        {
            get
            {
                return this._editable;
            }

            set
            {
                if (value != this._editable)
                {
                    this._editable = value;
                    this.NotifyPropertyChanged();
                }
            }
        }

        /// <summary>
        /// 获取或设置数据源对象。
        /// </summary>
        /// <returns></returns>
        [Browsable(false)]
        [DefaultValue(null)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual Entity DataSource
        {
            get => (Entity)this.bindingSource.DataSource;
            set
            {
                if (!Equals(this.bindingSource.DataSource, value))
                {
                    this.bindingSource.DataSource = value;
                    this.bindingSource.ResetCurrentItem();
                }
            }
        }

        protected readonly BindingSource bindingSource;

        /// <summary>
        /// 将挂起的更改应用于基础数据源。
        /// </summary>
        public void EndEdit()
        {
            this.bindingSource.EndEdit();
        }

        /// <summary>
        /// 获取编辑控件实际绑定的数据源。
        /// </summary>
        /// <returns></returns>
        public virtual object GetDataSource()
        {
            return this.bindingSource;
        }

        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public BindingFieldInfo[] Columns
        {
            get
            {
                if (this.m_columns is null)
                {
                    var items = new List<BindingFieldInfo>();
                    var members = typeof(Entity).GetMembers();
                    foreach (MemberInfo member in members)
                    {
                        BindingFieldInfo column;
                        if (member is FieldInfo field)
                        {
                            column = new BindingFieldInfo();
                            column.DataType = field.FieldType;
                        }
                        else if (member is PropertyInfo property)
                        {
                            column = new BindingFieldInfo();
                            column.DataType = property.PropertyType;
                        }
                        else
                        {
                            continue;
                        }

                        column.Name = member.Name;
                        column.ReadOnly = false;
                        var description = member.GetCustomAttribute<DescriptionAttribute>();
                        if (description is not null)
                            column.Caption = description.Description;
                        items.Add(column);
                    }
                    this.m_columns = items.ToArray();
                }
                return this.m_columns;
            }
        }

        private BindingFieldInfo[] m_columns;

        public virtual BindingFieldInfo[] GetColumnCollection()
        {
            return this.Columns;
        }

        private Dictionary<IBindingFieldNotification, string> _bindingFieldNotificationCollection;

        public void Subscription(string columnName, IBindingFieldNotification field)
        {
            if (this._bindingFieldNotificationCollection is null)
                this._bindingFieldNotificationCollection = new Dictionary<IBindingFieldNotification, string>();
            if (!this._bindingFieldNotificationCollection.ContainsKey(field))
            {
                this._bindingFieldNotificationCollection.Add(field, columnName);
            }
        }

        void IBindingFieldNotificationBus.Unsubscribe(IBindingFieldNotification field)
        {
            if (this._bindingFieldNotificationCollection is object && this._bindingFieldNotificationCollection.ContainsKey(field))
            {
                this._bindingFieldNotificationCollection.Remove(field);
            }
        }

        /// <summary>
        /// 通知属性变更
        /// </summary>
        public void NotifyPropertyChanged()
        {
            if (this._bindingFieldNotificationCollection is object)
            {
                foreach (var item in this._bindingFieldNotificationCollection)
                    item.Key.NotifyPropertyChanged();
            }
        }

        public BindingField Bind(Control control, string propertyName, string dataMember)
        {
            return new BindingField(control, propertyName, this, dataMember, true, DataSourceUpdateMode.OnValidation);
        }

        /// <summary>
        /// 添加绑定
        /// </summary>
        /// <param name="control"></param>
        /// <param name="propertyName"></param>
        /// <param name="dataMember"></param>
        /// <param name="dataSourceUpdateMode"></param>
        /// <returns></returns>
        public BindingField Bind(Control control, string propertyName, string dataMember, DataSourceUpdateMode dataSourceUpdateMode)
        {
            return new BindingField(control, propertyName, this, dataMember, true, dataSourceUpdateMode);
        }
    }
}